home *** CD-ROM | disk | FTP | other *** search
- /* #[info: */
- /************************************************************************
- * *
- * #### #### #### # # # ##### *
- * # # # # # # ## ## # *
- * # # # # # # # ## # # *
- * # ## # # # ## ####### # # ### *
- * # # # # # # # # # # # *
- * # # # # # # # # # # # *
- * #### #### #### # # # # ##### *
- * *
- * Jan van der Steen *
- * *
- * Centre for Mathematics and Computer Science *
- * Amsterdam, the Netherlands *
- * *
- *----------------------------------------------------------------------*
- * File : gogame.c *
- * Purpose : Implement functions to maintain a Go Game Data structure *
- * Version : 1.7 *
- * Modified: 3/15/93 11:26:56 *
- * Author : Jan van der Steen (jansteen@cwi.nl) *
- ************************************************************************/
- /* #]info: */
- /* #[include: */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include "tools.h"
- #include "gogame.h"
- #include "gorules.h"
-
- /* #]include: */
- /* #[prototype: */
-
- #ifdef __STDC__
- # define PROTO(s) s
- #else
- # define PROTO(s) ()
- #endif
-
- /*
- * Memory managers
- */
- static CHAIN * chain_alloc PROTO((void));
- static GOMOVE * move_alloc PROTO((void));
- static TEXT * text_alloc PROTO((void));
- static GOBAN * goban_alloc PROTO((int size));
- static int goban_loop PROTO(( GOBAN *goban,
- GONODE *gonode,
- int (*f)PROTO(( GOBAN *goban,
- GONODE *gonode
- ))
- ));
- static int goban_do PROTO(( GOBAN *goban,
- GONODE *gonode,
- int (*f)PROTO(( GOBAN *goban,
- GONODE *gonode
- ))
- ));
-
- #undef PROTO
-
- /* #]prototype: */
-
- /* #[text_alloc: */
-
- static TEXT *
- text_alloc()
- /*
- * Allocate a text list
- */
- {
- TEXT *text = (TEXT *) calloc(1, sizeof(TEXT));
-
- if (text == (TEXT *) 0) {
- fprintf(stderr, "Out of memory in text_alloc()\n");
- exit(1);
- }
- return text;
- }
-
- /* #]text_alloc: */
- /* #[text_store: */
-
- void
- text_store(gogame, line)
- /*
- * The way we deal with incomming text is very IGS specific.
- * Normally we would just append the text to the text buffer.
- * But, since in case of IGS it either contains "say" or "kibitz" messages
- * we have introduced a special format to store these typical cases.
- */
- GOGAME *gogame;
- char * line;
- {
- TEXT *text;
- TEXT *textlast = gogame->gm_movelast->mv_textlast;
- char *name;
- char *rank;
- char *mesg;
- char *space;
-
- /*
- * The kibitz messages have the format:
- *
- * <space><username> <rank><optional [space|*]>: <text>
- *
- * jansteen 3d: My ~connection~ is terrible... (old IGS)
- * jansteen 3d : My ~connection~ is terrible... (rank)
- * jansteen 3d*: My ~connection~ is terrible... (rating)
- */
- if (*line != ' ') {
- fprintf(stderr, "C[%s] didn't contain kibitz (skipped)\n", line);
- return;
- };
-
- text = text_alloc();
- if (gogame->gm_movelast->mv_text == (TEXT *) 0)
- gogame->gm_movelast->mv_text = text;
- else
- gogame->gm_movelast->mv_textlast->cc_next = text;
- gogame->gm_movelast->mv_textlast = text;
-
- name = ++line;
- rank = strchr(name, ' '); if (rank == (char *) 0) goto error;
- mesg = strchr(rank, ':'); if (mesg == (char *) 0) goto error;
- *rank = 0; rank += 1;
- *mesg = 0; mesg += 2;
- space = rank+strlen(rank)-1; if (*space == ' ') *space = 0;
- text->cc_name = string_store(name);
- text->cc_rank = string_store(rank);
- text->cc_text = string_store(mesg);
- text->cc_prev = textlast;
- return;
- error:
- fprintf(stderr, "Message seems a badly formatted kibitz (ignored)\n");
- }
-
- /* #]text_store: */
- /* #[goban_alloc: */
-
- static GOBAN *
- goban_alloc (size)
- /*
- * Allocate a new Go board
- */
- int size;
- {
- GOBAN * goban = (GOBAN *) calloc(1, sizeof(GOBAN));
-
- if (goban == (GOBAN *) 0) {
- fprintf(stderr, "Out of memory in goban_alloc()\n");
- exit(1);
- }
- if (size <= 0) {
- fprintf(stderr, "Board size out of range in goban_alloc(): %d\n", size);
- exit(1);
- }
- goban->gb_node = (GONODE *) calloc(size*size, sizeof(GONODE));
- if (goban->gb_node == (GONODE *) 0) {
- fprintf(stderr, "Out of memory in goban_alloc()\n");
- exit(1);
- }
-
- return goban;
- }
-
- /* #]goban_alloc: */
- /* #[goban_new: */
-
- GOBAN *
- goban_new(gogame, s)
- /*
- * Allocate and construct a new Go board
- */
- GOGAME *gogame;
- int s; /* Requested size */
- {
- PLACE p;
-
- gogame->gm_goban = goban_alloc(s);
- for (p = 0; p < s*s; p++) {
- NODE(gogame,p)->gn_n = NODE(gogame,p-s);
- NODE(gogame,p)->gn_e = NODE(gogame,p+1);
- NODE(gogame,p)->gn_s = NODE(gogame,p+s);
- NODE(gogame,p)->gn_w = NODE(gogame,p-1);
- }
- for (p = 0 ; p < s ; p++ ) NODE(gogame,p)->gn_n = (GONODE *) 0;
- for (p = s-1 ; p < s*s; p+=s) NODE(gogame,p)->gn_e = (GONODE *) 0;
- for (p = s*(s-1); p < s*s; p++ ) NODE(gogame,p)->gn_s = (GONODE *) 0;
- for (p = 0 ; p < s*s; p+=s) NODE(gogame,p)->gn_w = (GONODE *) 0;
-
- return gogame->gm_goban;
- }
-
- /* #]goban_new: */
- /* #[goban_loop: */
-
- #ifdef __STDC__
- # define PROTO(s) s
- #else
- # define PROTO(s) ()
- #endif
-
- static int
- goban_loop(goban, gonode, f)
- /*
- * Visit "all" goban points and execute f()
- */
- GOBAN *goban;
- GONODE *gonode;
- int (*f)PROTO((GOBAN *goban, GONODE *gonode));
- {
- (goban->gb_loop)++;
-
- return goban_do(goban, gonode, f);
- }
-
- #undef PROTO
-
- /* #]goban_loop: */
- /* #[goban_do: */
-
- #ifdef __STDC__
- # define PROTO(s) s
- #else
- # define PROTO(s) ()
- #endif
-
- static int
- goban_do(goban, gonode, f)
- /*
- * Visit "all" goban points and execute f()
- */
- GOBAN *goban;
- GONODE *gonode;
- int (*f)PROTO((GOBAN *goban, GONODE *gonode));
- {
- /*
- * On the goban?
- */
- if (gonode == (GONODE *) 0) return 0;
-
- /*
- * First time?
- */
- if (gonode->gn_loop == goban->gb_loop) return 0;
-
- /*
- * Mark goban point
- */
- gonode->gn_loop = goban->gb_loop;
-
- if (! (*f)(goban, gonode)) return 1;
-
- return (
- goban_do(goban, gonode->gn_n, f) +
- goban_do(goban, gonode->gn_e, f) +
- goban_do(goban, gonode->gn_s, f) +
- goban_do(goban, gonode->gn_w, f) + 1
- );
- }
-
- #undef PROTO
-
- /* #]goban_do: */
- /* #[chain_alloc: */
-
- static CHAIN *
- chain_alloc()
- /*
- * Allocate a new chain
- */
- {
- CHAIN *chain = (CHAIN *) calloc(1, sizeof(CHAIN));
-
- if (chain == (CHAIN *) 0) {
- fprintf(stderr, "Out of memory in chain_alloc()\n");
- exit(1);
- }
- return chain;
- }
-
- /* #]chain_alloc: */
- /* #[move_alloc: */
-
- static GOMOVE *
- move_alloc()
- /*
- * Allocate a new move
- */
- {
- GOMOVE *move = (GOMOVE *) calloc(1, sizeof(GOMOVE));
-
- if (move == (GOMOVE *) 0) {
- fprintf(stderr, "Out of memory in move_alloc()\n");
- exit(1);
- }
- return move;
- }
-
- /* #]move_alloc: */
- /* #[move_store: */
-
- void
- move_store(gogame, side, place)
- GOGAME *gogame;
- int side;
- PLACE place;
- {
- GOMOVE *move;
- GOMOVE *last;
- char *error;
-
- if (place == ('t'-'a') + ('t'-'a') * gogame->gm_size) {
- /*
- * The SGF format uses [tt] for passes,
- * i.e. a move outside the board.
- * We will just ignore them for the moment,
- * till we figured out how to deal with passes properly.
- */
- return;
- }
-
- move = move_alloc();
- last = gogame->gm_movelast;
- if (gogame->gm_move == (GOMOVE *) 0) gogame->gm_move = move;
- else gogame->gm_movelast->mv_next = move;
- gogame->gm_movelast = move;
-
- move->mv_place = NODE(gogame,place);
- move->mv_side = side;
- move->mv_nr = ++(gogame->gm_movenr);
- move->mv_prev = last;
- switch (rules_check(gogame, place, side)) {
- case RULES_LEGAL: return;
- case RULES_OUTSIDE: error = "Not on the board"; break;
- case RULES_SUICIDE: error = "Suicide move"; break;
- case RULES_KO: error = "Ko capture not allowed"; break;
- case RULES_OCCUPIED: error = "Occupied point"; break;
- default: error = "Unknown error"; break;
- }
- /*
- * Emit error info
- */
- fprintf(stderr, "Illegal move: %s (move: %d)\n", error, move->mv_nr);
-
- /*
- * Make the place vacant
- */
- move->mv_side = FREE;
- }
-
- /* #]move_store: */
- /* #[capture_store: */
-
- void
- capture_store(gogame, gonode)
- /*
- * Add the stone at gonode to the captured stone list
- */
- GOGAME *gogame;
- GONODE *gonode;
- {
- CHAIN *c = chain_alloc();
- GOMOVE *m = gogame->gm_movelast;
-
- c->ch_stone = gonode->gn_stone;
-
- /*
- * Insert the stone
- */
- c->ch_next = m->mv_captured;
- m->mv_captured = c;
- }
-
- /* #]capture_store: */
- /* #[game_alloc: */
-
- GOGAME *
- game_alloc()
- /*
- * Allocate a new game
- */
- {
- GOGAME *g = (GOGAME *) calloc(1, sizeof(GOGAME));
-
- if (g == (GOGAME *) 0) {
- fprintf(stderr, "Out of memory in game_alloc()\n");
- exit(1);
- }
- /*
- * Allocate Go board
- */
- g->gm_goban = goban_new(g, MAXSIZE);
- g->gm_size = MAXSIZE;
-
- return g;
- }
-
- /* #]game_alloc: */
- /* #[game_free: */
-
- void
- game_free(gogame)
- /*
- * Free the resources associated with a game
- */
- GOGAME *gogame;
- {
- GOGAME *g = gogame;
- GOMOVE *m;
- GOMOVE *mn;
- CHAIN *c;
- CHAIN *cn;
-
- /* Moves + captured stones */
- for (m = g->gm_move; m != (GOMOVE *) 0; m = mn) {
- mn = m->mv_next;
- for (c = m->mv_captured; c != (CHAIN *) 0; c = cn) {
- cn = c->ch_next;
- free(c);
- }
- free(m);
- }
-
- /* Goban + Gonodes */
- if (g->gm_goban != (GOBAN *) 0) {
- if (g->gm_goban->gb_node != (GONODE *) 0) free(g->gm_goban->gb_node);
- free(g->gm_goban);
- }
-
- /* Game information */
- if (g->gm_event != (char *) 0) free(g->gm_event );
- if (g->gm_user != (char *) 0) free(g->gm_user );
- if (g->gm_view != (char *) 0) free(g->gm_view );
- if (g->gm_game != (char *) 0) free(g->gm_game );
- if (g->gm_name != (char *) 0) free(g->gm_name );
- if (g->gm_black != (char *) 0) free(g->gm_black );
- if (g->gm_brank != (char *) 0) free(g->gm_brank );
- if (g->gm_btime != (char *) 0) free(g->gm_btime );
- if (g->gm_bleft != (char *) 0) free(g->gm_bleft );
- if (g->gm_white != (char *) 0) free(g->gm_white );
- if (g->gm_wrank != (char *) 0) free(g->gm_wrank );
- if (g->gm_wtime != (char *) 0) free(g->gm_wtime );
- if (g->gm_wleft != (char *) 0) free(g->gm_wleft );
- if (g->gm_komi != (char *) 0) free(g->gm_komi );
- if (g->gm_date != (char *) 0) free(g->gm_date );
- if (g->gm_place != (char *) 0) free(g->gm_place );
- if (g->gm_result != (char *) 0) free(g->gm_result );
- }
-
- /* #]game_free: */
-